﻿#include "stdafx.h"
#if _QG_USE_DX9
#include "qg_dx9_dev.h"
#include "qg_dx9_mtrl.h"
#include "qg_dx9_draw.h"
#include "qg_dx9_cnv.h"
#include "qg_trace.h"

// 그리기 바인드, 오류 검사 안함
bool DX9Device::_BindDraw(bool bindindex) throw()
{
	ksize_t i;

	// 꼭지점
	for (i = 0; i < QGSGVERT_MAX_VALUE; i++)
	{
		if (!_sprn->bd_vb[i])
			continue;

		_sprn->bd_vb[i] = false;

		DX9Buffer* buf = (DX9Buffer*)_sprn->w_vb[i];

		if (buf != NULL)
		{
			IDirect3DVertexBuffer9* vb = (IDirect3DVertexBuffer9*)buf->GetDescription();
#if 0
			kint stride = buf->GetStride();

			// 레이아웃과 비교
			if (stride != _sprn->w_lo->GetStageStride((kint)i))
			{
				// 폭이 틀리면 경고!!!
				kobj_tracep(KLV_WARNING, KERR_STRIDE);
			}

			_device->SetStreamSource(i, vb, 0, (UINT)stride);
#else
			kint stride = _sprn->w_lo->GetStageStride((kint)i);
			_device->SetStreamSource(i, vb, 0, stride);
#endif
		}
	}

	// 인덱스
	if (bindindex && _sprn->bd_ib)
	{
		_sprn->bd_ib = false;

		IDirect3DIndexBuffer9* ib = (IDirect3DIndexBuffer9*)_sprn->w_ib->GetDescription();
		_device->SetIndices(ib);
	}

	// 레이아웃
	if (_sprn->bd_lo)
	{
		_sprn->bd_lo = false;

		IDirect3DVertexDeclaration9* vd = (IDirect3DVertexDeclaration9*)_sprn->w_lo->GetDescription();
		_device->SetVertexDeclaration(vd);
	}

	// 토폴로지
	if (_sprn->bd_tpg)
		_sprn->bd_tpg = false;

	//
	return true;
}

// 버퍼 만들기
qgBuffer* DX9Device::_RoamImplBuffer(const qgArprBuffer& ap)
{
	if (ap.type == QGBUFFER_VERTEX)
	{
		DX9BufferVertex* buf = kobj_create(DX9BufferVertex);

		if (!buf->_Create(ap, this))
		{
			buf->Unload();
			buf = NULL;
		}

		return buf;
	}
	else if (ap.type == QGBUFFER_INDEX)
	{
		DX9BufferIndex* buf = kobj_create(DX9BufferIndex);

		if (!buf->_Create(ap, this))
		{
			buf->Unload();
			buf = NULL;
		}

		return buf;
	}
	else
	{
		// 인덱스도 아니고
		// 꼭지점도 아니면
		// 실패지 뭐...
		return NULL;
	}
}

// 레이아웃 만들기
qgLayout* DX9Device::_RoamImplLayout(qgLayoutData* data)
{
	DX9Layout* lo = kobj_create(DX9Layout);

	if (!lo->_Create(data))
	{
		lo->Unload();
		lo = NULL;
	}

	return lo;
}


//////////////////////////////////////////////////////////////////////////
// 버퍼
KIMPL_SUBCLASS_ABSTRACT(DX9Buffer, "AbstractDX9Buffer");

//
kint DX9Buffer::_ParseArpr(const qgArprBuffer& ap, DX9Device* dev)
{
	if (K_OFLAG(ap.flags, QGRESF_DYNAMIC))
	{
		_dx_usage = D3DUSAGE_DYNAMIC;
		_dx_pool = D3DPOOL_DEFAULT;
	}
	else
	{
		_dx_usage = K_OFLAG(ap.flags, QGRESF_LOCAL) ? 0 : D3DUSAGE_WRITEONLY;

		if (!dev->IsEx())
			_dx_pool = D3DPOOL_MANAGED;
		else
		{
			_dx_usage |= D3DUSAGE_DYNAMIC;
			_dx_pool = D3DPOOL_DEFAULT;
		}
	}

	if (K_OFLAG(ap.flags, QGRESF_DYNAMIC | QGRESF_INIT))
		_dx_usage |= D3DUSAGE_WRITEONLY;

	_buftype = ap.type;
	_flags = ap.flags;
	_count = ap.count;
	_stride = ap.stride;

	return ap.count * ap.stride;
}


//////////////////////////////////////////////////////////////////////////
// 꼭지점
KIMPL_SUBCLASS(DX9BufferVertex, "DX9BufferVertex");

//
DX9BufferVertex::DX9BufferVertex() throw()
{
}

//
DX9BufferVertex::~DX9BufferVertex() throw()
{
	if (_node_desc)
	{
		if (_lock)
			DX9BufferVertex::LeaveMap();

		IDirect3DVertexBuffer9* vb = (IDirect3DVertexBuffer9*)_node_desc;
		vb->Release();
	}
}

//
bool DX9BufferVertex::_Create(const qgArprBuffer& ap, DX9Device* dev)
{
	kint size = _ParseArpr(ap, dev);

	if (K_OFLAG(ap.flags, QGBUFF_PARTICLE))
		_dx_usage |= D3DUSAGE_POINTS;

	// 만들기
	IDirect3DVertexBuffer9* vb;
	HRESULT hr = dev->GetDevice()->CreateVertexBuffer(size, _dx_usage, 0, _dx_pool, &vb, NULL);

	if (FAILED(hr))
	{
		kobj_tracep(KLV_ERROR, KERR_FAIL_CREATE);
		return false;
	}

	// 저장
	_node_desc = (kuintptr)vb;

	// 데이터가 있으면 써주자
	if (ap.data)
	{
		kpointer ptr = DX9BufferVertex::EnterMap(QGENTF_NONE);
		if (ptr)
		{
			memcpy(ptr, ap.data, size);
			DX9BufferVertex::LeaveMap();
		}
	}

	return true;
}

//
kint DX9BufferVertex::Signal(const char* sig, kpointer arg)
{
	if (_dx_pool == D3DPOOL_DEFAULT)
	{
		if (k_streqv(sig, DX9_SIGNAL_LOST))
		{
			if (_node_desc)
			{
				IDirect3DVertexBuffer9* vb = (IDirect3DVertexBuffer9*)_node_desc;
				vb->Release();
				_node_desc = 0;
			}
		}
		else if (k_streqv(sig, DX9_SIGNAL_RESET))
		{
			DX9Device* dev = (DX9Device*)_dev;
			kint size = _stride * _count;
			IDirect3DVertexBuffer9* vb;
			HRESULT hr = dev->GetDevice()->CreateVertexBuffer(size, _dx_usage, 0, _dx_pool, &vb, NULL);

			_node_desc = (kuintptr)vb;

			if (FAILED(hr))
			{
				_node_desc = 0;
				return KERR_FAIL_CREATE;
			}
		}
	}

	return KERR_SUCCESS;
}

//
kpointer DX9BufferVertex::EnterMap(kint flags)
{
	// 잠겨 있으면 실패
	if (_lock)
	{
		kobj_tracep(KLV_ERROR, KERR_MAPPING);
		return NULL;
	}

	//
	if (K_OFLAG(flags, QGENTF_READ) && K_OFLAG(_dx_usage, D3DUSAGE_WRITEONLY))
	{
		// READ가 있는데 WRITEONLY면 할 수 없음
		kobj_tracep(KLV_ERROR, KERR_MAPPING);
		return NULL;
	}

	//
	IDirect3DVertexBuffer9* vb = (IDirect3DVertexBuffer9*)_node_desc;
	DWORD dw = _dx_pool == D3DPOOL_DEFAULT ? D3DLOCK_DISCARD : 0;

	kpointer ptr;
	HRESULT hr = vb->Lock(0, 0, &ptr, dw);

	if (FAILED(hr))
	{
		kobj_tracep(KLV_ERROR, KERR_MAPPING);
		return NULL;
	}

	//
	_lock = ptr;

	return ptr;
}

//
bool DX9BufferVertex::LeaveMap()
{
	if (!_lock)
	{
		// 잠겨있지 않음
		kobj_tracep(KLV_ERROR, KERR_MAPPING);
		return false;
	}

	//
	IDirect3DVertexBuffer9* vb = (IDirect3DVertexBuffer9*)_node_desc;
	HRESULT hr = vb->Unlock();

	if (FAILED(hr))
	{
		kobj_tracep(KLV_ERROR, KERR_MAPPING);
		return false;
	}

	//
	_lock = NULL;

	return true;
}


//////////////////////////////////////////////////////////////////////////
// 인덱스
KIMPL_SUBCLASS(DX9BufferIndex, "DX9BufferIndex");

//
DX9BufferIndex::DX9BufferIndex() throw()
{
}

//
DX9BufferIndex::~DX9BufferIndex() throw()
{
	if (_node_desc)
	{
		if (_lock)
			DX9BufferIndex::LeaveMap();

		IDirect3DIndexBuffer9* ib = (IDirect3DIndexBuffer9*)_node_desc;
		ib->Release();
	}
}

//
bool DX9BufferIndex::_Create(const qgArprBuffer& ap, DX9Device* dev)
{
	kint size = _ParseArpr(ap, dev);

	// 만들기
	D3DFORMAT fmt = ap.stride == 2 ? D3DFMT_INDEX16 : D3DFMT_INDEX32;

	IDirect3DIndexBuffer9* ib;
	HRESULT hr = dev->GetDevice()->CreateIndexBuffer(size, _dx_usage, fmt, _dx_pool, &ib, NULL);

	if (FAILED(hr))
	{
		kobj_tracep(KLV_ERROR, KERR_FAIL_CREATE);
		return false;
	}

	// 저장
	_node_desc = (kuintptr)ib;

	// 데이터가 있으면 써주자
	if (ap.data)
	{
		kpointer ptr = DX9BufferIndex::EnterMap(QGENTF_NONE);
		if (ptr)
		{
			memcpy(ptr, ap.data, size);
			DX9BufferIndex::LeaveMap();
		}
	}

	return true;
}

//
kint DX9BufferIndex::Signal(const char* sig, kpointer arg)
{
	if (_dx_pool == D3DPOOL_DEFAULT)
	{
		if (k_streqv(sig, DX9_SIGNAL_LOST))
		{
			if (_node_desc)
			{
				IDirect3DIndexBuffer9* ib = (IDirect3DIndexBuffer9*)_node_desc;
				ib->Release();
				_node_desc = 0;
			}
		}
		else if (k_streqv(sig, DX9_SIGNAL_RESET))
		{
			DX9Device* dev = (DX9Device*)_dev;
			kint size = _stride * _count;
			D3DFORMAT fmt = _stride == 2 ? D3DFMT_INDEX16 : D3DFMT_INDEX32;
			IDirect3DIndexBuffer9* ib;
			HRESULT hr = dev->GetDevice()->CreateIndexBuffer(size, _dx_usage, fmt, _dx_pool, &ib, NULL);

			_node_desc = (kuintptr)ib;

			if (FAILED(hr))
			{
				_node_desc = 0;
				return KERR_FAIL_CREATE;
			}
		}
	}

	return KERR_SUCCESS;
}

//
kpointer DX9BufferIndex::EnterMap(kint flags)
{
	// 잠겨 있으면 실패
	if (_lock)
	{
		kobj_tracep(KLV_ERROR, KERR_MAPPING);
		return NULL;
	}

	//
	if (K_OFLAG(flags, QGENTF_READ) && K_OFLAG(_dx_usage, D3DUSAGE_WRITEONLY))
	{
		// READ가 있는데 WRITEONLY면 할 수 없음
		kobj_tracep(KLV_ERROR, KERR_MAPPING);
		return NULL;
	}

	//
	IDirect3DIndexBuffer9* ib = (IDirect3DIndexBuffer9*)_node_desc;
	DWORD dw = _dx_pool == D3DPOOL_DEFAULT ? D3DLOCK_DISCARD : D3DLOCK_NOOVERWRITE;

	kpointer ptr;
	HRESULT hr = ib->Lock(0, 0, &ptr, dw);

	if (FAILED(hr))
	{
		kobj_tracep(KLV_ERROR, KERR_MAPPING);
		return NULL;
	}

	//
	_lock = ptr;

	return ptr;
}

//
bool DX9BufferIndex::LeaveMap()
{
	if (!_lock)
	{
		// 잠겨있지 않음
		kobj_tracep(KLV_ERROR, KERR_MAPPING);
		return false;
	}

	//
	IDirect3DIndexBuffer9* ib = (IDirect3DIndexBuffer9*)_node_desc;
	HRESULT hr = ib->Unlock();

	if (FAILED(hr))
	{
		kobj_tracep(KLV_ERROR, KERR_MAPPING);
		return false;
	}

	//
	_lock = NULL;

	return true;
}


//////////////////////////////////////////////////////////////////////////
// 레이아웃
KIMPL_SUBCLASS(DX9Layout, "DX9Layout");

//
DX9Layout::DX9Layout() throw()
{
}

//
DX9Layout::~DX9Layout() throw()
{
	if (_node_desc)
	{
		IDirect3DVertexDeclaration9* vd = (IDirect3DVertexDeclaration9*)_node_desc;
		vd->Release();
	}
}

//
D3DDECLUSAGE DX9Layout::_ConvSemanticUsage(qgDloUsage usage)
{
	static D3DDECLUSAGE sUsage[] =
	{
		D3DDECLUSAGE_POSITION,
		D3DDECLUSAGE_COLOR,
		D3DDECLUSAGE_BLENDWEIGHT,
		D3DDECLUSAGE_BLENDINDICES,
		D3DDECLUSAGE_NORMAL,
		D3DDECLUSAGE_TEXCOORD,
		D3DDECLUSAGE_TANGENT,
		D3DDECLUSAGE_BINORMAL,
		D3DDECLUSAGE_POSITIONT,			// DX9에만 존재한다
	};

	static_assert(K_COUNTOF(sUsage) >= QGDLOU_MAX_VALUE, "invalid layout type count");

	k_assert(usage >= 0 && usage < K_COUNTOF(sUsage), "range error");

	return sUsage[usage];
}

//
D3DDECLTYPE DX9Layout::_ConvType(qgDloType type)
{
	static D3DDECLTYPE sType[] =
	{
		D3DDECLTYPE_FLOAT1,
		D3DDECLTYPE_FLOAT2,
		D3DDECLTYPE_FLOAT3,
		D3DDECLTYPE_FLOAT4,
		D3DDECLTYPE_FLOAT1,		// INT 형 지원 안함
		D3DDECLTYPE_FLOAT2,		// INT 형 지원 안함
		D3DDECLTYPE_FLOAT3,		// INT 형 지원 안함
		D3DDECLTYPE_FLOAT4,		// INT 형 지원 안함
		D3DDECLTYPE_FLOAT16_2,
		D3DDECLTYPE_FLOAT16_4,
		D3DDECLTYPE_UNUSED,		// BYTE2 지원 안함
		D3DDECLTYPE_UBYTE4,
		D3DDECLTYPE_USHORT2N,
		D3DDECLTYPE_USHORT4N,
		D3DDECLTYPE_D3DCOLOR,
	};

	k_assert(type >= 0 && type < K_COUNTOF(sType), "range error");

	return sType[type];
}

//
bool DX9Layout::_Create(qgLayoutData* data)
{
	DX9Device* dev = (DX9Device*)_dev;
	kint i, cnt = data->elms.Count();
	kushort size, offset[QGSGVERT_MAX_VALUE] = {0, 0, 0, 0};

	_descs.Resize(cnt + 1);

	// 루프
	for (i = 0; i < cnt; i++)
	{
		D3DVERTEXELEMENT9& d = _descs[i];
		qgLayoutElement& e = data->elms[i];

		//
		size = (kushort)qgResSupp::LayoutTypeToSize(e.type);

		// 추가 데이터 처리
		e.offset = offset[e.slot];
		e.size = size;

		// 기본
		d.Stream = e.slot;
		d.Type = (BYTE)_ConvType(e.type);
		d.Method = D3DDECLMETHOD_DEFAULT;
		d.Usage = (BYTE)_ConvSemanticUsage(e.usage);
		d.UsageIndex = e.index;
		d.Offset = e.offset;

		// 오프셋 전진
		offset[e.slot] += size;
	}

	// 마지막 비워줘야 함
	D3DVERTEXELEMENT9& ld = _descs[cnt];
#if _MSC_VER >= 1800
	ld = D3DDECL_END();
#else
	static D3DVERTEXELEMENT9 sEndDecl = D3DDECL_END();
	ld = sEndDecl;
#endif

	// 만들기
	IDirect3DVertexDeclaration9* vd;
	if (FAILED(dev->GetDevice()->CreateVertexDeclaration(_descs.Data(), &vd)))
	{
		kobj_tracep(KLV_ERROR, KERR_FAIL_CREATE);
		return false;
	}

	// [2014-3-3 KIM] 아... 이걸 왜 빼먹어서 시간 낭비를... ㅠ_ㅜ
	_node_desc = (kuintptr)vd;

	// 키
	data->elms.Swap(_data.elms);

	// [2014-3-2 KIM] 값 
	for (_stride = i = 0; i < QGSGVERT_MAX_VALUE; i++)
	{
		_stride += offset[i];
		_stage[i] = offset[i];
	}

	return true;
}

#endif	// _QG_USE_DX9
